#include "std/vga.h"
#include "std/debug.h"
#include "arch/cpu.h"
#include "arch/traps.h"
#include "arch/inout.h"
#include "kernel/irq.h"
#include "kernel/vma.h"

// global variables
uint irq_err_count = 0;
irq_desc_t irq_desc[NR_IRQS];
irq_cpu_stat_t irq_stat[NR_CPUS] = {0};

static void set_gate(gate_node_t *gate, uint type, uint dpl, uint addr)
{
	uint lower, higher;

	kassert(0 != addr);
	lower = (KERNEL_CS << 16) | (addr & 0xFFFF);
	higher = (addr & 0xFFFF0000) | (0x8000 + (dpl << 13) + (type << 8));
	gate->low = lower;
	gate->high = higher;
}

static void set_intr_gate(uint gid, uint addr)
{
	kassert(gid < 256);
	set_gate(&idt_table[gid], 14, 0, addr);
}

static void set_trap_gate(uint gid, uint addr)
{
	kassert(gid < 256);
	set_gate(&idt_table[gid], 15, 0, addr);
}

void blue_screen(void)
{
	vga_setcolor((1 << 4) | 7);
	vga_clrscr();
	vga_putchar('\n');
	vga_puts("A problem has been detected and viskrnl has been shut down to\n");
	vga_puts("prevent damage to the computer.\n\n");

	go32_die_endless();
}

void handle_divide_error(regs_t regs)
{

}

void handle_page_fault(regs_t regs)
{
	vma_t *vma;
	int ret = 0;
	uint address;

	/* get the address */
	__asm__("movl %%cr2,%0":"=r" (address));

	vma = find_vma_by_addr(&kernel_vma_mgr, address);
	if (vma)
	{
		struct vma_operations* ops = vma->vma_ops;
		if (ops && ops->handle_page_fault)
		{
			ret = vma->vma_ops->handle_page_fault(&regs, vma, address);
			if (!ret) return;
		}
	}

	// handle user space faults
handle_failure:
	blue_screen();
}

static uint startup_none(uint irq) { return 0; }
static void other_none(uint irq) {}
#define shutdown_none	other_none
#define enable_none		other_none
#define disable_none	other_none
#define ack_none		other_none
#define end_none		other_none

irq_controller_t no_irq_controller =
{
	"none",
	startup_none,
	shutdown_none,
	enable_none,
	disable_none,
	ack_none,
	end_none
};

extern irq_controller_t i8259A_irq_controller;

static void init_ISA_irqs(void)
{
	int i;

	init_8259A();

	for (i = 0; i < NR_IRQS; i++)
	{
		irq_desc[i].status = IRQ_DISABLED;
		irq_desc[i].action = NULL;
		

		if (i < 16) {
			//16 old-style INTA-cycle interrupts:
			irq_desc[i].handler = &i8259A_irq_controller;
		} else {
			irq_desc[i].handler = &no_irq_controller;
		}
	}
}

// global initialization
void i386_init_traps(void)
{
	int i;
	uint addr = (uint)default_intr_hdr;

	// disable interrupts
	cli();

	// init i8259A
	init_ISA_irqs();

	for (i = 0; i < 256; ++i)
	{
		set_intr_gate(i, addr);
		addr += 8;
	}

	// initialize the trap gates
	set_trap_gate(0, (uint)divide_error_trap_hdr);
/*
	set_trap_gate(3,&int3);
	set_trap_gate (4,&overflow);
	set_trap_gate (5,&bounds);
*/
	set_trap_gate(6, (uint)invalid_instruction_trap_hdr);
/*
	set_trap_gate(7,&device_not_available);
	set_trap_gate(8,&double_fault);
	set_trap_gate(9,&coprocessor_segment_overrun);
	set_trap_gate(10,&invalid_tss);
	set_trap_gate(11,&segment_not_present);
	set_trap_gate(12,&stack_segment);
*/
	set_trap_gate(13, (uint)gpf_trap_hdr);
	set_trap_gate(14, (uint)page_fault_trap_hdr);
//	set_trap_gate(16,&coprocessor_error);

	// enable interrupts
	sti();

}

/* EOF */
